package com.milo.config;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.milo.common.utils.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

import javax.sql.DataSource;
import java.util.Arrays;


/**
 * druid 配置数据源
 * @author milogenius
 * @date 2020/4/21 15:05
 *
 */
@Slf4j
@Configuration
public class DruidConfig
{
    @Value("${spring.datasource.druid.initialSize}")
    private int initialSize; //初始化时建立物理连接的个数

    @Value("${spring.datasource.druid.minIdle}")
    private int minIdle;//最小连接池数量

    @Value("${spring.datasource.druid.maxActive}")
    private int maxActive;//最大连接池数量

    @Value("${spring.datasource.druid.maxWait}")
    private int maxWait;//获取连接时最大等待时间

    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;//1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据，详细看testWhileIdle属性的说明

    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;//配置一个连接在池中最小生存的时间，单位毫秒

    @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
    private int maxEvictableIdleTimeMillis;//配置一个连接在池中最大生存的时间，单位是毫秒

    @Value("${spring.datasource.druid.validationQuery}")
    private String validationQuery;//用来检测连接是否有效的sql，要求是一个查询语句。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会其作用。

    @Value("${spring.datasource.druid.testWhileIdle}")
    private boolean testWhileIdle;//申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。建议配置为true，不影响性能，并且保证安全性

    @Value("${spring.datasource.druid.testOnBorrow}")
    private boolean testOnBorrow;//申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。

    @Value("${spring.datasource.druid.testOnReturn}")
    private boolean testOnReturn;//归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能


    @Value("${spring.datasource.driverClassName}")
    private String driverClassName;

    @Value("${spring.datasource.druid.url}")
    private String url;

    @Value("${spring.datasource.druid.username}")
    private String username;

    @Value("${spring.datasource.druid.password}")
    private String password;

    @Value("${spring.datasource.druid.filters}")
    private String filters;

    @Value("${spring.datasource.druid.connection-properties}")
    private String connectionProperties;
    @Value("${spring.datasource.druid.connection-error-retry-attempts}")
    private int errorRetryAttempts;



    @Value("${spring.datasource.druid.stat-view-servlet.url-pattern}")
    private String servleturlPattern;
    @Value("${spring.datasource.druid.stat-view-servlet.allow}")
    private String servletAllow;
    @Value("${spring.datasource.druid.stat-view-servlet.login-username}")
    private String servletUserName;
    @Value("${spring.datasource.druid.stat-view-servlet.login-password}")
    private String servletPassword;
    @Value("${spring.datasource.druid.filter.stat.log-slow-sql}")
    private boolean logSlowSql;
    @Value("${spring.datasource.druid.filter.stat.slow-sql-millis}")
    private int slowSqlMillis;




    @Value("${spring.datasource.druid.web-stat-filter.exclusions}")
    private String filterExclusions="";
    @Value("${spring.datasource.druid.web-stat-filter.profile-enable}")
    private String filterProfileEnable;
    @Value("${spring.datasource.druid.web-stat-filter.url-pattern}")
    private String filterUrlPattern;


    private DruidDataSource dataSource(DruidDataSource datasource)
    {
        try {
            /** 配置初始化大小、最小、最大 */
            datasource.setInitialSize(initialSize);
            datasource.setMaxActive(maxActive);
            datasource.setMinIdle(minIdle);

            /** 配置获取连接等待超时的时间 */
            datasource.setMaxWait(maxWait);

            /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

            /** 配置一个连接在池中最小、最大生存的时间，单位是毫秒 */
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

            /**
             * 用来检测连接是否有效的sql，要求是一个查询语句，常用select 'x'。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
             */
            datasource.setValidationQuery(validationQuery);
            /** 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。 */
            datasource.setTestWhileIdle(testWhileIdle);
            /** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
            datasource.setTestOnBorrow(testOnBorrow);
            /** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
            datasource.setTestOnReturn(testOnReturn);

            datasource.setDriverClassName(driverClassName);
            datasource.setUrl(url);
            datasource.setUsername(AESUtil.aesDecode(username));
            datasource.setPassword(AESUtil.aesDecode(password));
            /** 配置扩展插件 */
            datasource.setFilters(filters);//配置扩展插件

            datasource.setConnectionProperties(connectionProperties);
            datasource.setConnectionErrorRetryAttempts(errorRetryAttempts);

            datasource.setProxyFilters(Arrays.asList(statFilter()));
            log.info("datasource configuration initialization success");
            return datasource;
        } catch (Exception e) {
            log.error("datasource configuration initialization error", e);
        }
        return null;
    }

    /**
     * 注册一个StatViewServlet
     * @return ServletRegistrationBean
     */
    private ServletRegistrationBean druidServletInit() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        //添加初始化参数：initParams

        //这里可以访问http://localhost:8080/{servleturlPattern}/index.html进行访问填入密码后可以看到管理
        reg.addUrlMappings(servleturlPattern);
        reg.addInitParameter("loginUsername", AESUtil.aesDecode(servletUserName));//登录查看信息的账号
        reg.addInitParameter("loginPassword", AESUtil.aesDecode(servletPassword));//登录查看信息的账号密码.
        reg.addInitParameter("resetEnable","false"); //是否能够重置数据.
        reg.addInitParameter("allow", servletAllow); //白名单

//        //IP黑名单 (存在共同时，deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
//        reg.addInitParameter("deny","192.168.1.73");

        return reg;
    }

    /**
     *  注册一个：filterRegistrationBean
     * @return
     */
    private FilterRegistrationBean filterRegistrationBeanInit() {
        //创建过滤器
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        //设置过滤器过滤路径
        filterRegistrationBean.addUrlPatterns(filterUrlPattern);
        //忽略过滤的形式
        filterRegistrationBean.addInitParameter("exclusions", filterExclusions);
        filterRegistrationBean.addInitParameter("profileEnable", filterProfileEnable);
        return filterRegistrationBean;
    }



    @Bean
    public Filter statFilter() {
        StatFilter statFilter = new StatFilter();
        statFilter.setSlowSqlMillis(slowSqlMillis);
        statFilter.setLogSlowSql(logSlowSql);
        return statFilter;
    }

    @Bean(name = "dataSource")
    public DataSource masterDataSource()
    {
        DruidDataSource dataSource = new DruidDataSource();
        return this.dataSource(dataSource);
    }


    @Bean
    public ServletRegistrationBean druidServlet() {
        return druidServletInit();
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        return this.filterRegistrationBeanInit();
    }

    /**
     * 必须加上static
     */
    @Bean
    public static PropertySourcesPlaceholderConfigurer loadProperties() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        return configurer;
    }

}
